package edu.pdx.cs.guavadata.domain;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.lang.Class;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.lang.String;


/**
 *
 * @author VuTran
 */
public abstract class Domain {

    public abstract boolean validate(Object o);

    public abstract Object convertString(String s) throws DomainConversionException;
    
    //public abstract Type NaiveType {get;} 
    private Class NaiveType;
    public abstract Class getNaiveType();
    
    private String DBType;
    public abstract String getDBType();
    
    public abstract Domain upcast();

    public abstract Object upcastValue(Object o) throws Exception;

    public abstract Domain merge(Domain d);
    
    private boolean IsOrdered;
    public abstract boolean isOrdered();
    
    private boolean CanContain;
    public abstract boolean canContain();
            
    private Domain Clone;
    @Override
    public abstract Domain clone();
    
    public List<Object> listOutput() {
        return null;
    }

    public static Domain findRoot(Domain d1, Domain d2) {
        if (d1.equals(d2)) {
            return d1;
        }
        Domain dtemp = d1.merge(d2);
        if (dtemp != null) {
            return dtemp;
        }

        // Create two upcast stacks
        List<Domain> ld1 = new ArrayList<Domain>();
        List<Domain> ld2 = new ArrayList<Domain>();

        dtemp = d1;
        while (dtemp != null) {
            ld1.add(dtemp);
            dtemp = dtemp.upcast();
        }

        dtemp = d2;
        while (dtemp != null) {
            ld2.add(dtemp);
            dtemp = dtemp.upcast();
        }

        // Reverse the lists of upcasts, so that the tops of the trees are first
        Collections.reverse(ld1);
        Collections.reverse(ld2);

        int length = Math.min(ld1.size(), ld2.size());

        // Test the treetops, and if they cannot merge, bail
        Domain candidate = ld1.get(0).merge(ld2.get(0));
        if (candidate == null) {
            return null;
        }

        // Work down the upcast lists, and find the first pair that don't merge 
        for (int iIndex = 1; iIndex < length; iIndex++) {
            Domain cTemp = ld1.get(iIndex).merge(ld2.get(iIndex));
            if (cTemp == null) {
                return candidate;
            }
            candidate = cTemp;
        }

        // If we get to here, then the candidate should be returned
        // This is because one of the domains is in the other's upcast list
        return candidate;
    }
    
    /*public virtual System.Windows.Forms.DataGridViewCell GridCellTemplate
    {
        get
        {
            return null;
        }
    }
 -- couldn't find the appropriate componnent in java*/ 
    
    public abstract Element ToXml(Document xdoc);

    //public abstract XElement ToXElement(); -- what?

    public static Domain fromDBInfoRow(ResultSet dr) {
        //var type = dr["Data_Type"].ToString();
        String type = null;
        try {
            type = dr.getString("Data Type");
        } catch (Exception e) {
            System.err .println("EXCEPTION: fromDBInforRow!" + e.getMessage());
        }
        
        if (type.equals("bit")) {
            return Boolean.fromDBInfoRow(dr);
        } else if (type.equals("bigint")) {
            return BigInteger.fromDBInfoRow(dr);
        } else if (type.equals("int")) {
            return Integer.fromDBInfoRow(dr);
        } else if (type.equals("smallint")) {
            return SmallInteger.fromDBInfoRow(dr);
        } else if (type.equals("tinyint")) {
            return TinyInteger.fromDBInfoRow(dr);
        } else if (type.equals("uniqueidentifier")) {
            return UniqueIdentifier.fromDBInfoRow(dr);
        } else if (type.equals("varchar")) {
            return edu.pdx.cs.guavadata.domain.String.fromDBInfoRow(dr);
        } else if (type.equals("nvarchar")) {
            return edu.pdx.cs.guavadata.domain.String.fromDBInfoRow(dr);
        } else if (type.equals("text")) {
            return edu.pdx.cs.guavadata.domain.String.fromDBInfoRow(dr);
        } else if (type.equals("datetime")) {
            return DateTime.fromDBInfoRow(dr);
        } else if (type.equals("float")) {
            return edu.pdx.cs.guavadata.domain.Double.fromDBInfoRow(dr);
        } else if (type.equals("decimal")) {
            return Decimal.fromDBInfoRow(dr);
        } else if (type.equals("real")) {
            return Single.fromDBInfoRow(dr);
        }
        return null;
    }

    
    public static Domain fromElement(Element xe) {
        if (xe.getNodeName() != "Domain") {
            return null;
        }

        // What type should the new domain be?
        String type = xe.getAttribute("Type");

        if (type.equals("Boolean")) {
            return Boolean.fromElement(xe);
        } else if (type.equals("BigInteger")) {
            return BigInteger.fromElement(xe);
        } else if (type.equals("Integer")) {
            return Integer.fromElement(xe);
        } else if (type.equals("SmallInteger")) {
            return SmallInteger.fromElement(xe);
        } else if (type.equals("TinyInteger")) {
            return TinyInteger.fromElement(xe);
        } else if (type.equals("UniqueIdentifier")) {
            return UniqueIdentifier.fromElement(xe);
        } else if (type.equals("String")) {
            return edu.pdx.cs.guavadata.domain.String.fromElement(xe);
        } else if (type.equals("DateTime")) {
            return DateTime.fromElement(xe);
        } else if (type.equals("Decimal")) {
            return Decimal.fromElement(xe);
        } else if (type.equals("Single")) {
            return Single.fromElement(xe);
        } else if (type.equals("Double")) {
            return Double.fromElement(xe);
        } else if (type.equals("Enumerated")) {
            return Enumerated.fromElement(xe);
        } else if (type.equals("Lookup")) {
            return Lookup.fromElement(xe);
        }
        return null;
    }
     
     
}

